/***************************************************************************
 *                             PacketHandlers.cs
 *                            -------------------
 *   begin                : May 1, 2002
 *   copyright            : (C) The RunUO Software Team
 *   email                : info@runuo.com
 *
 *   $Id: PacketHandlers.cs 266 2007-11-27 02:28:55Z zippy $
 *
 ***************************************************************************/

/***************************************************************************
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 ***************************************************************************/

using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Server.Accounting;
using Server.Gumps;
using Server.Targeting;
using Server.Items;
using Server.Menus;
using Server.Mobiles;
using Server.Movement;
using Server.Prompts;
using Server.HuePickers;
using Server.ContextMenus;
using Server.Diagnostics;
using CV = Server.ClientVersion;

namespace Server.Network
{
	public enum MessageType
	{
		Regular = 0x00,
		System = 0x01,
		Emote = 0x02,
		Label = 0x06,
		Focus = 0x07,
		Whisper = 0x08,
		Yell = 0x09,
		Spell = 0x0A,

		Guild = 0x0D,
		Alliance = 0x0E,
		Command = 0x0F,

		Encoded = 0xC0
	}

	public class PacketHandlers
	{
		private static PacketHandler[] m_Handlers;
		private static PacketHandler[] m_6017Handlers;

		private static PacketHandler[] m_ExtendedHandlersLow;
		private static Dictionary<int, PacketHandler> m_ExtendedHandlersHigh;

		private static EncodedPacketHandler[] m_EncodedHandlersLow;
		private static Dictionary<int, EncodedPacketHandler> m_EncodedHandlersHigh;

		public static PacketHandler[] Handlers
		{
			get { return m_Handlers; }
		}

		static PacketHandlers()
		{
			m_Handlers = new PacketHandler[0x100];
			m_6017Handlers = new PacketHandler[0x100];

			m_ExtendedHandlersLow = new PacketHandler[0x100];
			m_ExtendedHandlersHigh = new Dictionary<int, PacketHandler>();

			m_EncodedHandlersLow = new EncodedPacketHandler[0x100];
			m_EncodedHandlersHigh = new Dictionary<int, EncodedPacketHandler>();

			Register(0x00, 104, false, new OnPacketReceive(CreateCharacter));
			Register(0x01, 5, false, new OnPacketReceive(Disconnect));
			Register(0x02, 7, true, new OnPacketReceive(MovementReq));
			Register(0x03, 0, true, new OnPacketReceive(AsciiSpeech));
			Register(0x04, 2, true, new OnPacketReceive(GodModeRequest));
			Register(0x05, 5, true, new OnPacketReceive(AttackReq));
			Register(0x06, 5, true, new OnPacketReceive(UseReq));
			Register(0x07, 7, true, new OnPacketReceive(LiftReq));
			Register(0x08, 14, true, new OnPacketReceive(DropReq));
			Register(0x09, 5, true, new OnPacketReceive(LookReq));
			Register(0x0A, 11, true, new OnPacketReceive(Edit));
			Register(0x12, 0, true, new OnPacketReceive(TextCommand));
			Register(0x13, 10, true, new OnPacketReceive(EquipReq));
			Register(0x14, 6, true, new OnPacketReceive(ChangeZ));
			Register(0x22, 3, true, new OnPacketReceive(Resynchronize));
			Register(0x2C, 2, true, new OnPacketReceive(DeathStatusResponse));
			Register(0x34, 10, true, new OnPacketReceive(MobileQuery));
			Register(0x3A, 0, true, new OnPacketReceive(ChangeSkillLock));
			Register(0x3B, 0, true, new OnPacketReceive(VendorBuyReply));
			Register(0x47, 11, true, new OnPacketReceive(NewTerrain));
			Register(0x48, 73, true, new OnPacketReceive(NewAnimData));
			Register(0x58, 106, true, new OnPacketReceive(NewRegion));
			Register(0x5D, 73, false, new OnPacketReceive(PlayCharacter));
			Register(0x61, 9, true, new OnPacketReceive(DeleteStatic));
			Register(0x6C, 19, true, new OnPacketReceive(TargetResponse));
			Register(0x6F, 0, true, new OnPacketReceive(SecureTrade));
			Register(0x72, 5, true, new OnPacketReceive(SetWarMode));
			Register(0x73, 2, false, new OnPacketReceive(PingReq));
			Register(0x75, 35, true, new OnPacketReceive(RenameRequest));
			Register(0x79, 9, true, new OnPacketReceive(ResourceQuery));
			Register(0x7E, 2, true, new OnPacketReceive(GodviewQuery));
			Register(0x7D, 13, true, new OnPacketReceive(MenuResponse));
			Register(0x80, 62, false, new OnPacketReceive(AccountLogin));
			Register(0x83, 39, false, new OnPacketReceive(DeleteCharacter));
			Register(0x91, 65, false, new OnPacketReceive(GameLogin));
			Register(0x95, 9, true, new OnPacketReceive(HuePickerResponse));
			Register(0x96, 0, true, new OnPacketReceive(GameCentralMoniter));
			Register(0x98, 0, true, new OnPacketReceive(MobileNameRequest));
			Register(0x9A, 0, true, new OnPacketReceive(AsciiPromptResponse));
			Register(0x9B, 258, true, new OnPacketReceive(HelpRequest));
			Register(0x9D, 51, true, new OnPacketReceive(GMSingle));
			Register(0x9F, 0, true, new OnPacketReceive(VendorSellReply));
			Register(0xA0, 3, false, new OnPacketReceive(PlayServer));
			Register(0xA4, 149, false, new OnPacketReceive(SystemInfo));
			Register(0xA7, 4, true, new OnPacketReceive(RequestScrollWindow));
			Register(0xAD, 0, true, new OnPacketReceive(UnicodeSpeech));
			Register(0xB1, 0, true, new OnPacketReceive(DisplayGumpResponse));
			Register(0xB5, 64, true, new OnPacketReceive(ChatRequest));
			Register(0xB6, 9, true, new OnPacketReceive(ObjectHelpRequest));
			Register(0xB8, 0, true, new OnPacketReceive(ProfileReq));
			Register(0xBB, 9, false, new OnPacketReceive(AccountID));
			Register(0xBD, 0, false, new OnPacketReceive(ClientVersion));
			Register(0xBE, 0, true, new OnPacketReceive(AssistVersion));
			Register(0xBF, 0, true, new OnPacketReceive(ExtendedCommand));
			Register(0xC2, 0, true, new OnPacketReceive(UnicodePromptResponse));
			Register(0xC8, 2, true, new OnPacketReceive(SetUpdateRange));
			Register(0xC9, 6, true, new OnPacketReceive(TripTime));
			Register(0xCA, 6, true, new OnPacketReceive(UTripTime));
			Register(0xCF, 0, false, new OnPacketReceive(AccountLogin));
			Register(0xD0, 0, true, new OnPacketReceive(ConfigurationFile));
			Register(0xD1, 2, true, new OnPacketReceive(LogoutReq));
			Register(0xD6, 0, true, new OnPacketReceive(BatchQueryProperties));
			Register(0xD7, 0, true, new OnPacketReceive(EncodedCommand));
			Register(0xEF, 21, false, new OnPacketReceive(LoginServerSeed));

			Register6017(0x08, 15, true, new OnPacketReceive(DropReq6017));

			RegisterExtended(0x05, false, new OnPacketReceive(ScreenSize));
			RegisterExtended(0x06, true, new OnPacketReceive(PartyMessage));
			RegisterExtended(0x07, true, new OnPacketReceive(QuestArrow));
			RegisterExtended(0x09, true, new OnPacketReceive(DisarmRequest));
			RegisterExtended(0x0A, true, new OnPacketReceive(StunRequest));
			RegisterExtended(0x0B, false, new OnPacketReceive(Language));
			RegisterExtended(0x0C, true, new OnPacketReceive(CloseStatus));
			RegisterExtended(0x0E, true, new OnPacketReceive(Animate));
			RegisterExtended(0x0F, false, new OnPacketReceive(Empty)); // What's this?
			RegisterExtended(0x10, true, new OnPacketReceive(QueryProperties));
			RegisterExtended(0x13, true, new OnPacketReceive(ContextMenuRequest));
			RegisterExtended(0x15, true, new OnPacketReceive(ContextMenuResponse));
			RegisterExtended(0x1A, true, new OnPacketReceive(StatLockChange));
			RegisterExtended(0x1C, true, new OnPacketReceive(CastSpell));
			RegisterExtended(0x24, false, new OnPacketReceive(UnhandledBF));

			RegisterEncoded(0x19, true, new OnEncodedPacketReceive(SetAbility));
			RegisterEncoded(0x28, true, new OnEncodedPacketReceive(GuildGumpRequest));

			RegisterEncoded(0x32, true, new OnEncodedPacketReceive(QuestGumpRequest));
		}

		public static void Register(int packetID, int length, bool ingame, OnPacketReceive onReceive)
		{
			m_Handlers[packetID] = new PacketHandler(packetID, length, ingame, onReceive);

			if (m_6017Handlers[packetID] == null)
				m_6017Handlers[packetID] = new PacketHandler(packetID, length, ingame, onReceive);
		}

		public static PacketHandler GetHandler(int packetID)
		{
			return m_Handlers[packetID];
		}

		public static void Register6017(int packetID, int length, bool ingame, OnPacketReceive onReceive)
		{
			m_6017Handlers[packetID] = new PacketHandler(packetID, length, ingame, onReceive);
		}

		public static PacketHandler Get6017Handler(int packetID)
		{
			return m_6017Handlers[packetID];
		}

		public static void RegisterExtended(int packetID, bool ingame, OnPacketReceive onReceive)
		{
			if (packetID >= 0 && packetID < 0x100)
				m_ExtendedHandlersLow[packetID] = new PacketHandler(packetID, 0, ingame, onReceive);
			else
				m_ExtendedHandlersHigh[packetID] = new PacketHandler(packetID, 0, ingame, onReceive);
		}

		public static PacketHandler GetExtendedHandler(int packetID)
		{
			if (packetID >= 0 && packetID < 0x100)
				return m_ExtendedHandlersLow[packetID];
			else
			{
				PacketHandler handler;
				m_ExtendedHandlersHigh.TryGetValue(packetID, out handler);
				return handler;
			}
		}

		public static void RemoveExtendedHandler(int packetID)
		{
			if (packetID >= 0 && packetID < 0x100)
				m_ExtendedHandlersLow[packetID] = null;
			else
				m_ExtendedHandlersHigh.Remove(packetID);
		}

		public static void RegisterEncoded(int packetID, bool ingame, OnEncodedPacketReceive onReceive)
		{
			if (packetID >= 0 && packetID < 0x100)
				m_EncodedHandlersLow[packetID] = new EncodedPacketHandler(packetID, ingame, onReceive);
			else
				m_EncodedHandlersHigh[packetID] = new EncodedPacketHandler(packetID, ingame, onReceive);
		}

		public static EncodedPacketHandler GetEncodedHandler(int packetID)
		{
			if (packetID >= 0 && packetID < 0x100)
				return m_EncodedHandlersLow[packetID];
			else
			{
				EncodedPacketHandler handler;
				m_EncodedHandlersHigh.TryGetValue(packetID, out handler);
				return handler;
			}
		}

		public static void RemoveEncodedHandler(int packetID)
		{
			if (packetID >= 0 && packetID < 0x100)
				m_EncodedHandlersLow[packetID] = null;
			else
				m_EncodedHandlersHigh.Remove(packetID);
		}

		public static void RegisterThrottler(int packetID, ThrottlePacketCallback t)
		{
			PacketHandler ph = GetHandler(packetID);

			if (ph != null)
				ph.ThrottleCallback = t;

			ph = Get6017Handler(packetID);

			if (ph != null)
				ph.ThrottleCallback = t;
		}

		private static void UnhandledBF(NetState state, PacketReader pvSrc)
		{
		}

		public static void Empty(NetState state, PacketReader pvSrc)
		{
		}

		public static void SetAbility(NetState state, IEntity e, EncodedReader reader)
		{
			EventSink.InvokeSetAbility(new SetAbilityEventArgs(state.Mobile, reader.ReadInt32()));
		}

		public static void GuildGumpRequest(NetState state, IEntity e, EncodedReader reader)
		{
			EventSink.InvokeGuildGumpRequest(new GuildGumpRequestArgs(state.Mobile));
		}

		public static void QuestGumpRequest(NetState state, IEntity e, EncodedReader reader)
		{
			EventSink.InvokeQuestGumpRequest(new QuestGumpRequestArgs(state.Mobile));
		}

		public static void EncodedCommand(NetState state, PacketReader pvSrc)
		{
			IEntity e = World.FindEntity(pvSrc.ReadInt32());
			int packetID = pvSrc.ReadUInt16();

			EncodedPacketHandler ph = GetEncodedHandler(packetID);

			if (ph != null)
			{
				if (ph.Ingame && state.Mobile == null)
				{
					Console.WriteLine("Client: {0}: Sent ingame packet (0xD7x{1:X2}) before having been attached to a mobile", state, packetID);
					state.Dispose();
				}
				else if (ph.Ingame && state.Mobile.Deleted)
				{
					state.Dispose();
				}
				else
				{
					ph.OnReceive(state, e, new EncodedReader(pvSrc));
				}
			}
			else
			{
				pvSrc.Trace(state);
			}
		}

		public static void RenameRequest(NetState state, PacketReader pvSrc)
		{
			Mobile from = state.Mobile;
			Mobile targ = World.FindMobile(pvSrc.ReadInt32());

			if (targ != null)
				EventSink.InvokeRenameRequest(new RenameRequestEventArgs(from, targ, pvSrc.ReadStringSafe()));
		}

		public static void ChatRequest(NetState state, PacketReader pvSrc)
		{
			EventSink.InvokeChatRequest(new ChatRequestEventArgs(state.Mobile));
		}

		public static void SecureTrade(NetState state, PacketReader pvSrc)
		{
			switch (pvSrc.ReadByte())
			{
				case 1: // Cancel
					{
						Serial serial = pvSrc.ReadInt32();

						SecureTradeContainer cont = World.FindItem(serial) as SecureTradeContainer;

						if (cont != null && cont.Trade != null && (cont.Trade.From.Mobile == state.Mobile || cont.Trade.To.Mobile == state.Mobile))
							cont.Trade.Cancel();

						break;
					}
				case 2: // Check
					{
						Serial serial = pvSrc.ReadInt32();

						SecureTradeContainer cont = World.FindItem(serial) as SecureTradeContainer;

						if (cont != null)
						{
							SecureTrade trade = cont.Trade;

							bool value = (pvSrc.ReadInt32() != 0);

							if (trade != null && trade.From.Mobile == state.Mobile)
							{
								trade.From.Accepted = value;
								trade.Update();
							}
							else if (trade != null && trade.To.Mobile == state.Mobile)
							{
								trade.To.Accepted = value;
								trade.Update();
							}
						}

						break;
					}
			}
		}

		public static void VendorBuyReply(NetState state, PacketReader pvSrc)
		{
			pvSrc.Seek(1, SeekOrigin.Begin);

			int msgSize = pvSrc.ReadUInt16();
			Mobile vendor = World.FindMobile(pvSrc.ReadInt32());
			byte flag = pvSrc.ReadByte();

			if (vendor == null)
			{
				return;
			}
			else if (vendor.Deleted || !Utility.RangeCheck(vendor.Location, state.Mobile.Location, 10))
			{
				state.Send(new EndVendorBuy(vendor));
				return;
			}

			if (flag == 0x02)
			{
				msgSize -= 1 + 2 + 4 + 1;

				if ((msgSize / 7) > 100)
					return;

				ArrayList buyList = new ArrayList(msgSize / 7);
				for (; msgSize > 0; msgSize -= 7)
				{
					byte layer = pvSrc.ReadByte();
					Serial serial = pvSrc.ReadInt32();
					int amount = pvSrc.ReadInt16();

					buyList.Add(new BuyItemResponse(serial, amount));
				}

				if (buyList.Count > 0)
				{
					IVendor v = vendor as IVendor;

					if (v != null && v.OnBuyItems(state.Mobile, buyList))
						state.Send(new EndVendorBuy(vendor));
				}
			}
			else
			{
				state.Send(new EndVendorBuy(vendor));
			}
		}

		public static void VendorSellReply(NetState state, PacketReader pvSrc)
		{
			Serial serial = pvSrc.ReadInt32();
			Mobile vendor = World.FindMobile(serial);

			if (vendor == null)
			{
				return;
			}
			else if (vendor.Deleted || !Utility.RangeCheck(vendor.Location, state.Mobile.Location, 10))
			{
				state.Send(new EndVendorSell(vendor));
				return;
			}

			int count = pvSrc.ReadUInt16();
			if (count < 100 && pvSrc.Size == (1 + 2 + 4 + 2 + (count * 6)))
			{
				ArrayList sellList = new ArrayList(count);

				for (int i = 0; i < count; i++)
				{
					Item item = World.FindItem(pvSrc.ReadInt32());
					int Amount = pvSrc.ReadInt16();

					if (item != null && Amount > 0)
						sellList.Add(new SellItemResponse(item, Amount));
				}

				if (sellList.Count > 0)
				{
					IVendor v = vendor as IVendor;

					if (v != null && v.OnSellItems(state.Mobile, sellList))
						state.Send(new EndVendorSell(vendor));
				}
			}
		}

		public static void DeleteCharacter(NetState state, PacketReader pvSrc)
		{
			pvSrc.Seek(30, SeekOrigin.Current);
			int index = pvSrc.ReadInt32();

			EventSink.InvokeDeleteRequest(new DeleteRequestEventArgs(state, index));
		}

		public static void ResourceQuery(NetState state, PacketReader pvSrc)
		{
			if (VerifyGC(state))
			{
			}
		}

		public static void GameCentralMoniter(NetState state, PacketReader pvSrc)
		{
			if (VerifyGC(state))
			{
				int type = pvSrc.ReadByte();
				int num1 = pvSrc.ReadInt32();

				Console.WriteLine("God Client: {0}: Game central moniter", state);
				Console.WriteLine(" - Type: {0}", type);
				Console.WriteLine(" - Number: {0}", num1);

				pvSrc.Trace(state);
			}
		}

		public static void GodviewQuery(NetState state, PacketReader pvSrc)
		{
			if (VerifyGC(state))
			{
				Console.WriteLine("God Client: {0}: Godview query 0x{1:X}", state, pvSrc.ReadByte());
			}
		}

		public static void GMSingle(NetState state, PacketReader pvSrc)
		{
			if (VerifyGC(state))
				pvSrc.Trace(state);
		}

		public static void DeathStatusResponse(NetState state, PacketReader pvSrc)
		{
			// Ignored
		}

		public static void ObjectHelpRequest(NetState state, PacketReader pvSrc)
		{
			Mobile from = state.Mobile;

			Serial serial = pvSrc.ReadInt32();
			int unk = pvSrc.ReadByte();
			string lang = pvSrc.ReadString(3);

			if (serial.IsItem)
			{
				Item item = World.FindItem(serial);

				if (item != null && from.Map == item.Map && Utility.InUpdateRange(item.GetWorldLocation(), from.Location) && from.CanSee(item))
					item.OnHelpRequest(from);
			}
			else if (serial.IsMobile)
			{
				Mobile m = World.FindMobile(serial);

				if (m != null && from.Map == m.Map && Utility.InUpdateRange(m.Location, from.Location) && from.CanSee(m))
					m.OnHelpRequest(m);
			}
		}

		public static void MobileNameRequest(NetState state, PacketReader pvSrc)
		{
			Mobile m = World.FindMobile(pvSrc.ReadInt32());

			if (m != null && Utility.InUpdateRange(state.Mobile, m) && state.Mobile.CanSee(m))
				state.Send(new MobileName(m));
		}

		public static void RequestScrollWindow(NetState state, PacketReader pvSrc)
		{
			int lastTip = pvSrc.ReadInt16();
			int type = pvSrc.ReadByte();
		}

		public static void AttackReq(NetState state, PacketReader pvSrc)
		{
			Mobile from = state.Mobile;
			Mobile m = World.FindMobile(pvSrc.ReadInt32());

			if (m != null)
				from.Attack(m);
		}

		public static void HuePickerResponse(NetState state, PacketReader pvSrc)
		{
			int serial = pvSrc.ReadInt32();
			int value = pvSrc.ReadInt16();
			int hue = pvSrc.ReadInt16() & 0x3FFF;

			hue = Utility.ClipDyedHue(hue);

			foreach (HuePicker huePicker in state.HuePickers)
			{
				if (huePicker.Serial == serial)
				{
					state.RemoveHuePicker(huePicker);

					huePicker.OnResponse(hue);

					break;
				}
			}
		}

		public static void TripTime(NetState state, PacketReader pvSrc)
		{
			int unk1 = pvSrc.ReadByte();
			int unk2 = pvSrc.ReadInt32();

			state.Send(new TripTimeResponse(unk1));
		}

		public static void UTripTime(NetState state, PacketReader pvSrc)
		{
			int unk1 = pvSrc.ReadByte();
			int unk2 = pvSrc.ReadInt32();

			state.Send(new UTripTimeResponse(unk1));
		}

		public static void ChangeZ(NetState state, PacketReader pvSrc)
		{
			if (VerifyGC(state))
			{
				int x = pvSrc.ReadInt16();
				int y = pvSrc.ReadInt16();
				int z = pvSrc.ReadSByte();

				Console.WriteLine("God Client: {0}: Change Z ({1}, {2}, {3})", state, x, y, z);
			}
		}

		public static void SystemInfo(NetState state, PacketReader pvSrc)
		{
			int v1 = pvSrc.ReadByte();
			int v2 = pvSrc.ReadUInt16();
			int v3 = pvSrc.ReadByte();
			string s1 = pvSrc.ReadString(32);
			string s2 = pvSrc.ReadString(32);
			string s3 = pvSrc.ReadString(32);
			string s4 = pvSrc.ReadString(32);
			int v4 = pvSrc.ReadUInt16();
			int v5 = pvSrc.ReadUInt16();
			int v6 = pvSrc.ReadInt32();
			int v7 = pvSrc.ReadInt32();
			int v8 = pvSrc.ReadInt32();
		}

		public static void Edit(NetState state, PacketReader pvSrc)
		{
			if (VerifyGC(state))
			{
				int type = pvSrc.ReadByte(); // 10 = static, 7 = npc, 4 = dynamic
				int x = pvSrc.ReadInt16();
				int y = pvSrc.ReadInt16();
				int id = pvSrc.ReadInt16();
				int z = pvSrc.ReadSByte();
				int hue = pvSrc.ReadUInt16();

				Console.WriteLine("God Client: {0}: Edit {6} ({1}, {2}, {3}) 0x{4:X} (0x{5:X})", state, x, y, z, id, hue, type);
			}
		}

		public static void DeleteStatic(NetState state, PacketReader pvSrc)
		{
			if (VerifyGC(state))
			{
				int x = pvSrc.ReadInt16();
				int y = pvSrc.ReadInt16();
				int z = pvSrc.ReadInt16();
				int id = pvSrc.ReadUInt16();

				Console.WriteLine("God Client: {0}: Delete Static ({1}, {2}, {3}) 0x{4:X}", state, x, y, z, id);
			}
		}

		public static void NewAnimData(NetState state, PacketReader pvSrc)
		{
			if (VerifyGC(state))
			{
				Console.WriteLine("God Client: {0}: New tile animation", state);

				pvSrc.Trace(state);
			}
		}

		public static void NewTerrain(NetState state, PacketReader pvSrc)
		{
			if (VerifyGC(state))
			{
				int x = pvSrc.ReadInt16();
				int y = pvSrc.ReadInt16();
				int id = pvSrc.ReadUInt16();
				int width = pvSrc.ReadInt16();
				int height = pvSrc.ReadInt16();

				Console.WriteLine("God Client: {0}: New Terrain ({1}, {2})+({3}, {4}) 0x{5:X4}", state, x, y, width, height, id);
			}
		}

		public static void NewRegion(NetState state, PacketReader pvSrc)
		{
			if (VerifyGC(state))
			{
				string name = pvSrc.ReadString(40);
				int unk = pvSrc.ReadInt32();
				int x = pvSrc.ReadInt16();
				int y = pvSrc.ReadInt16();
				int width = pvSrc.ReadInt16();
				int height = pvSrc.ReadInt16();
				int zStart = pvSrc.ReadInt16();
				int zEnd = pvSrc.ReadInt16();
				string desc = pvSrc.ReadString(40);
				int soundFX = pvSrc.ReadInt16();
				int music = pvSrc.ReadInt16();
				int nightFX = pvSrc.ReadInt16();
				int dungeon = pvSrc.ReadByte();
				int light = pvSrc.ReadInt16();

				Console.WriteLine("God Client: {0}: New Region '{1}' ('{2}')", state, name, desc);
			}
		}

		public static void AccountID(NetState state, PacketReader pvSrc)
		{
		}

		public static bool VerifyGC(NetState state)
		{
			if (state.Mobile == null || state.Mobile.AccessLevel <= AccessLevel.Counselor)
			{
				if (state.Running)
					Console.WriteLine("Warning: {0}: Player using godclient, disconnecting", state);

				state.Dispose();
				return false;
			}
			else
			{
				return true;
			}
		}

		public static void TextCommand(NetState state, PacketReader pvSrc)
		{
			int type = pvSrc.ReadByte();
			string command = pvSrc.ReadString();

			Mobile m = state.Mobile;

			switch (type)
			{
				case 0x00: // Go
					{
						if (VerifyGC(state))
						{
							try
							{
								string[] split = command.Split(' ');

								int x = Utility.ToInt32(split[0]);
								int y = Utility.ToInt32(split[1]);

								int z;

								if (split.Length >= 3)
									z = Utility.ToInt32(split[2]);
								else if (m.Map != null)
									z = m.Map.GetAverageZ(x, y);
								else
									z = 0;

								m.Location = new Point3D(x, y, z);
							}
							catch
							{
							}
						}

						break;
					}
				case 0xC7: // Animate
					{
						EventSink.InvokeAnimateRequest(new AnimateRequestEventArgs(m, command));

						break;
					}
				case 0x24: // Use skill
					{
						int skillIndex;

						try { skillIndex = Convert.ToInt32(command.Split(' ')[0]); }
						catch { break; }

						Skills.UseSkill(m, skillIndex);

						break;
					}
				case 0x43: // Open spellbook
					{
						int booktype;

						try { booktype = Convert.ToInt32(command); }
						catch { booktype = 1; }

						EventSink.InvokeOpenSpellbookRequest(new OpenSpellbookRequestEventArgs(m, booktype));

						break;
					}
				case 0x27: // Cast spell from book
					{
						string[] split = command.Split(' ');

						if (split.Length > 0)
						{
							int spellID = Utility.ToInt32(split[0]) - 1;
							int serial = split.Length > 1 ? Utility.ToInt32(split[1]) : -1;

							EventSink.InvokeCastSpellRequest(new CastSpellRequestEventArgs(m, spellID, World.FindItem(serial)));
						}

						break;
					}
				case 0x58: // Open door
					{
						EventSink.InvokeOpenDoorMacroUsed(new OpenDoorMacroEventArgs(m));

						break;
					}
				case 0x56: // Cast spell from macro
					{
						int spellID = Utility.ToInt32(command) - 1;

						EventSink.InvokeCastSpellRequest(new CastSpellRequestEventArgs(m, spellID, null));

						break;
					}
				case 0xF4: // Invoke virtues from macro
					{
						int virtueID = Utility.ToInt32(command) - 1;

						EventSink.InvokeVirtueMacroRequest(new VirtueMacroRequestEventArgs(m, virtueID));

						break;
					}
				default:
					{
						Console.WriteLine("Client: {0}: Unknown text-command type 0x{1:X2}: {2}", state, type, command);
						break;
					}
			}
		}

		public static void GodModeRequest(NetState state, PacketReader pvSrc)
		{
			if (VerifyGC(state))
			{
				state.Send(new GodModeReply(pvSrc.ReadBoolean()));
			}
		}

		public static void AsciiPromptResponse(NetState state, PacketReader pvSrc)
		{
			int serial = pvSrc.ReadInt32();
			int prompt = pvSrc.ReadInt32();
			int type = pvSrc.ReadInt32();
			string text = pvSrc.ReadStringSafe();

			if (text.Length > 128)
				return;

			Mobile from = state.Mobile;
			Prompt p = from.Prompt;

			if (p != null && p.Serial == serial && p.Serial == prompt)
			{
				from.Prompt = null;

				if (type == 0)
					p.OnCancel(from);
				else
					p.OnResponse(from, text);
			}
		}

		public static void UnicodePromptResponse(NetState state, PacketReader pvSrc)
		{
			int serial = pvSrc.ReadInt32();
			int prompt = pvSrc.ReadInt32();
			int type = pvSrc.ReadInt32();
			string lang = pvSrc.ReadString(4);
			string text = pvSrc.ReadUnicodeStringLESafe();

			if (text.Length > 128)
				return;

			Mobile from = state.Mobile;
			Prompt p = from.Prompt;

			if (p != null && p.Serial == serial && p.Serial == prompt)
			{
				from.Prompt = null;

				if (type == 0)
					p.OnCancel(from);
				else
					p.OnResponse(from, text);
			}
		}

		public static void MenuResponse(NetState state, PacketReader pvSrc)
		{
			int serial = pvSrc.ReadInt32();
			int menuID = pvSrc.ReadInt16(); // unused in our implementation
			int index = pvSrc.ReadInt16();
			int itemID = pvSrc.ReadInt16();
			int hue = pvSrc.ReadInt16();

			index -= 1; // convert from 1-based to 0-based

			foreach (IMenu menu in state.Menus)
			{
				if (menu.Serial == serial)
				{
					state.RemoveMenu(menu);

					if (index >= 0 && index < menu.EntryLength)
					{
						menu.OnResponse(state, index);
					}
					else
					{
						menu.OnCancel(state);
					}

					break;
				}
			}
		}

		public static void ProfileReq(NetState state, PacketReader pvSrc)
		{
			int type = pvSrc.ReadByte();
			Serial serial = pvSrc.ReadInt32();

			Mobile beholder = state.Mobile;
			Mobile beheld = World.FindMobile(serial);

			if (beheld == null)
				return;

			switch (type)
			{
				case 0x00: // display request
					{
						EventSink.InvokeProfileRequest(new ProfileRequestEventArgs(beholder, beheld));

						break;
					}
				case 0x01: // edit request
					{
						pvSrc.ReadInt16(); // Skip
						int length = pvSrc.ReadUInt16();

						if (length > 511)
							return;

						string text = pvSrc.ReadUnicodeString(length);

						EventSink.InvokeChangeProfileRequest(new ChangeProfileRequestEventArgs(beholder, beheld, text));

						break;
					}
			}
		}

		public static void Disconnect(NetState state, PacketReader pvSrc)
		{
			int minusOne = pvSrc.ReadInt32();
		}

		public static void LiftReq(NetState state, PacketReader pvSrc)
		{
			Serial serial = pvSrc.ReadInt32();
			int amount = pvSrc.ReadUInt16();
			Item item = World.FindItem(serial);

			bool rejected;
			LRReason reject;

			state.Mobile.Lift(item, amount, out rejected, out reject);
		}

		public static void EquipReq(NetState state, PacketReader pvSrc)
		{
			Mobile from = state.Mobile;
			Item item = from.Holding;

			bool valid = (item != null && item.HeldBy == from && item.Map == Map.Internal);

			from.Holding = null;

			if (!valid)
			{
				return;
			}

			pvSrc.Seek(5, SeekOrigin.Current);
			Mobile to = World.FindMobile(pvSrc.ReadInt32());

			if (to == null)
				to = from;

			if (!to.AllowEquipFrom(from) || !to.EquipItem(item))
				item.Bounce(from);

			item.ClearBounce();
		}

		public static void DropReq(NetState state, PacketReader pvSrc)
		{
			pvSrc.ReadInt32(); // serial, ignored
			int x = pvSrc.ReadInt16();
			int y = pvSrc.ReadInt16();
			int z = pvSrc.ReadSByte();
			Serial dest = pvSrc.ReadInt32();

			Point3D loc = new Point3D(x, y, z);

			Mobile from = state.Mobile;

			if (dest.IsMobile)
				from.Drop(World.FindMobile(dest), loc);
			else if (dest.IsItem)
				from.Drop(World.FindItem(dest), loc);
			else
				from.Drop(loc);
		}

		public static void DropReq6017(NetState state, PacketReader pvSrc)
		{
			pvSrc.ReadInt32(); // serial, ignored
			int x = pvSrc.ReadInt16();
			int y = pvSrc.ReadInt16();
			int z = pvSrc.ReadSByte();
			pvSrc.ReadByte(); // Grid Location?
			Serial dest = pvSrc.ReadInt32();

			Point3D loc = new Point3D(x, y, z);

			Mobile from = state.Mobile;

			if (dest.IsMobile)
				from.Drop(World.FindMobile(dest), loc);
			else if (dest.IsItem)
				from.Drop(World.FindItem(dest), loc);
			else
				from.Drop(loc);
		}

		public static void ConfigurationFile(NetState state, PacketReader pvSrc)
		{
		}

		public static void LogoutReq(NetState state, PacketReader pvSrc)
		{
			state.Send(new LogoutAck());
		}

		public static void ChangeSkillLock(NetState state, PacketReader pvSrc)
		{
			Skill s = state.Mobile.Skills[pvSrc.ReadInt16()];

			if (s != null)
				s.SetLockNoRelay((SkillLock)pvSrc.ReadByte());
		}

		public static void HelpRequest(NetState state, PacketReader pvSrc)
		{
			EventSink.InvokeHelpRequest(new HelpRequestEventArgs(state.Mobile));
		}

		public static void TargetResponse(NetState state, PacketReader pvSrc)
		{
			int type = pvSrc.ReadByte();
			int targetID = pvSrc.ReadInt32();
			int flags = pvSrc.ReadByte();
			Serial serial = pvSrc.ReadInt32();
			int x = pvSrc.ReadInt16(), y = pvSrc.ReadInt16(), z = pvSrc.ReadInt16();
			int graphic = pvSrc.ReadInt16();

			if (targetID == unchecked((int)0xDEADBEEF))
				return;

			Mobile from = state.Mobile;

			Target t = from.Target;

			if (t != null)
			{
				TargetProfile prof = TargetProfile.Acquire(t.GetType());

				if (prof != null)
				{
					prof.Start();
				}

				try
				{
					if (x == -1 && y == -1 && !serial.IsValid)
					{
						// User pressed escape
						t.Cancel(from, TargetCancelType.Canceled);
					}
					else
					{
						object toTarget;

						if (type == 1)
						{
							if (graphic == 0)
							{
								toTarget = new LandTarget(new Point3D(x, y, z), from.Map);
							}
							else
							{
								Map map = from.Map;

								if (map == null || map == Map.Internal)
								{
									t.Cancel(from, TargetCancelType.Canceled);
									return;
								}
								else
								{
									Tile[] tiles = map.Tiles.GetStaticTiles(x, y, !t.DisallowMultis);

									bool valid = false;

									for (int i = 0; !valid && i < tiles.Length; ++i)
									{
										if (tiles[i].Z == z && (tiles[i].ID & 0x3FFF) == (graphic & 0x3FFF))
											valid = true;
									}

									if (!valid)
									{
										t.Cancel(from, TargetCancelType.Canceled);
										return;
									}
									else
									{
										toTarget = new StaticTarget(new Point3D(x, y, z), graphic);
									}
								}
							}
						}
						else if (serial.IsMobile)
						{
							toTarget = World.FindMobile(serial);
						}
						else if (serial.IsItem)
						{
							toTarget = World.FindItem(serial);
						}
						else
						{
							t.Cancel(from, TargetCancelType.Canceled);
							return;
						}

						t.Invoke(from, toTarget);
					}
				}
				finally
				{
					if (prof != null)
					{
						prof.Finish();
					}
				}
			}
		}

		public static void DisplayGumpResponse(NetState state, PacketReader pvSrc)
		{
			int serial = pvSrc.ReadInt32();
			int typeID = pvSrc.ReadInt32();
			int buttonID = pvSrc.ReadInt32();

			foreach (Gump gump in state.Gumps)
			{
				if (gump.Serial == serial && gump.TypeID == typeID)
				{
					int switchCount = pvSrc.ReadInt32();

					if (switchCount < 0 || switchCount > gump.m_Switches)
					{
						state.WriteConsole("Invalid gump response, disconnecting...");
						state.Dispose();
						return;
					}

					int[] switches = new int[switchCount];

					for (int j = 0; j < switches.Length; ++j)
						switches[j] = pvSrc.ReadInt32();

					int textCount = pvSrc.ReadInt32();

					if (textCount < 0 || textCount > gump.m_TextEntries)
					{
						state.WriteConsole("Invalid gump response, disconnecting...");
						state.Dispose();
						return;
					}

					TextRelay[] textEntries = new TextRelay[textCount];

					for (int j = 0; j < textEntries.Length; ++j)
					{
						int entryID = pvSrc.ReadUInt16();
						int textLength = pvSrc.ReadUInt16();

						if (textLength > 239)
						{
							state.WriteConsole("Invalid gump response, disconnecting...");
							state.Dispose();
							return;
						}

						string text = pvSrc.ReadUnicodeStringSafe(textLength);
						textEntries[j] = new TextRelay(entryID, text);
					}

					state.RemoveGump(gump);

					GumpProfile prof = GumpProfile.Acquire(gump.GetType());

					if (prof != null)
					{
						prof.Start();
					}

					gump.OnResponse(state, new RelayInfo(buttonID, switches, textEntries));

					if (prof != null)
					{
						prof.Finish();
					}

					return;
				}
			}

			if (typeID == 461)
			{ // Virtue gump
				int switchCount = pvSrc.ReadInt32();

				if (buttonID == 1 && switchCount > 0)
				{
					Mobile beheld = World.FindMobile(pvSrc.ReadInt32());

					if (beheld != null)
					{
						EventSink.InvokeVirtueGumpRequest(new VirtueGumpRequestEventArgs(state.Mobile, beheld));
					}
				}
				else
				{
					Mobile beheld = World.FindMobile(serial);

					if (beheld != null)
					{
						EventSink.InvokeVirtueItemRequest(new VirtueItemRequestEventArgs(state.Mobile, beheld, buttonID));
					}
				}
			}
		}

		public static void SetWarMode(NetState state, PacketReader pvSrc)
		{
			state.Mobile.DelayChangeWarmode(pvSrc.ReadBoolean());
		}

		public static void Resynchronize(NetState state, PacketReader pvSrc)
		{
			Mobile m = state.Mobile;

			state.Send(new MobileUpdate(m));
			state.Send(new MobileIncoming(m, m));

			m.SendEverything();

			state.Sequence = 0;

			m.ClearFastwalkStack();
		}

		private static int[] m_EmptyInts = new int[0];

		public static void AsciiSpeech(NetState state, PacketReader pvSrc)
		{
			Mobile from = state.Mobile;

			MessageType type = (MessageType)pvSrc.ReadByte();
			int hue = pvSrc.ReadInt16();
			pvSrc.ReadInt16(); // font
			string text = pvSrc.ReadStringSafe().Trim();

			if (text.Length <= 0 || text.Length > 128)
				return;

			if (!Enum.IsDefined(typeof(MessageType), type))
				type = MessageType.Regular;

			from.DoSpeech(text, m_EmptyInts, type, Utility.ClipDyedHue(hue));
		}

		private static KeywordList m_KeywordList = new KeywordList();

		public static void UnicodeSpeech(NetState state, PacketReader pvSrc)
		{
			Mobile from = state.Mobile;

			MessageType type = (MessageType)pvSrc.ReadByte();
			int hue = pvSrc.ReadInt16();
			pvSrc.ReadInt16(); // font
			string lang = pvSrc.ReadString(4);
			string text;

			bool isEncoded = (type & MessageType.Encoded) != 0;
			int[] keywords;

			if (isEncoded)
			{
				int value = pvSrc.ReadInt16();
				int count = (value & 0xFFF0) >> 4;
				int hold = value & 0xF;

				if (count < 0 || count > 50)
					return;

				KeywordList keyList = m_KeywordList;

				for (int i = 0; i < count; ++i)
				{
					int speechID;

					if ((i & 1) == 0)
					{
						hold <<= 8;
						hold |= pvSrc.ReadByte();
						speechID = hold;
						hold = 0;
					}
					else
					{
						value = pvSrc.ReadInt16();
						speechID = (value & 0xFFF0) >> 4;
						hold = value & 0xF;
					}

					if (!keyList.Contains(speechID))
						keyList.Add(speechID);
				}

				text = pvSrc.ReadUTF8StringSafe();

				keywords = keyList.ToArray();
			}
			else
			{
				text = pvSrc.ReadUnicodeStringSafe();

				keywords = m_EmptyInts;
			}

			text = text.Trim();

			if (text.Length <= 0 || text.Length > 128)
				return;

			type &= ~MessageType.Encoded;

			if (!Enum.IsDefined(typeof(MessageType), type))
				type = MessageType.Regular;

			from.Language = lang;
			from.DoSpeech(text, keywords, type, Utility.ClipDyedHue(hue));
		}

		public static void UseReq(NetState state, PacketReader pvSrc)
		{
			Mobile from = state.Mobile;

			if (from.AccessLevel >= AccessLevel.Counselor || DateTime.Now >= from.NextActionTime)
			{
				int value = pvSrc.ReadInt32();

				if ((value & ~0x7FFFFFFF) != 0)
				{
					from.OnPaperdollRequest();
				}
				else
				{
					Serial s = value;

					if (s.IsMobile)
					{
						Mobile m = World.FindMobile(s);

						if (m != null && !m.Deleted)
							from.Use(m);
					}
					else if (s.IsItem)
					{
						Item item = World.FindItem(s);

						if (item != null && !item.Deleted)
							from.Use(item);
					}
				}

				from.NextActionTime = DateTime.Now + TimeSpan.FromSeconds(0.5);
			}
			else
			{
				from.SendActionMessage();
			}
		}

		private static bool m_SingleClickProps;

		public static bool SingleClickProps
		{
			get { return m_SingleClickProps; }
			set { m_SingleClickProps = value; }
		}

		public static void LookReq(NetState state, PacketReader pvSrc)
		{
			Mobile from = state.Mobile;

			Serial s = pvSrc.ReadInt32();

			if (s.IsMobile)
			{
				Mobile m = World.FindMobile(s);

				if (m != null && from.CanSee(m) && Utility.InUpdateRange(from, m))
				{
					if (m_SingleClickProps)
					{
						m.OnAosSingleClick(from);
					}
					else
					{
						if (from.Region.OnSingleClick(from, m))
							m.OnSingleClick(from);
					}
				}
			}
			else if (s.IsItem)
			{
				Item item = World.FindItem(s);

				if (item != null && !item.Deleted && from.CanSee(item) && Utility.InUpdateRange(from.Location, item.GetWorldLocation()))
				{
					if (m_SingleClickProps)
					{
						item.OnAosSingleClick(from);
					}
					else if (from.Region.OnSingleClick(from, item))
					{
						if (item.Parent is Item)
							((Item)item.Parent).OnSingleClickContained(from, item);

						item.OnSingleClick(from);
					}
				}
			}
		}

		public static void PingReq(NetState state, PacketReader pvSrc)
		{
			state.Send(PingAck.Instantiate(pvSrc.ReadByte()));
		}

		public static void SetUpdateRange(NetState state, PacketReader pvSrc)
		{
			state.Send(ChangeUpdateRange.Instantiate(18));
		}

		private const int BadFood = unchecked((int)0xBAADF00D);
		private const int BadUOTD = unchecked((int)0xFFCEFFCE);

		public static void MovementReq(NetState state, PacketReader pvSrc)
		{
			Direction dir = (Direction)pvSrc.ReadByte();
			int seq = pvSrc.ReadByte();
			int key = pvSrc.ReadInt32();

			Mobile m = state.Mobile;

			if ((state.Sequence == 0 && seq != 0) || !m.Move(dir))
			{
				state.Send(new MovementRej(seq, m));
				state.Sequence = 0;

				m.ClearFastwalkStack();
			}
			else
			{
				++seq;

				if (seq == 256)
					seq = 1;

				state.Sequence = seq;
			}
		}

		public static int[] m_ValidAnimations = new int[]
			{
				6, 21, 32, 33,
				100, 101, 102,
				103, 104, 105,
				106, 107, 108,
				109, 110, 111,
				112, 113, 114,
				115, 116, 117,
				118, 119, 120,
				121, 123, 124,
				125, 126, 127,
				128
			};

		public static int[] ValidAnimations { get { return m_ValidAnimations; } set { m_ValidAnimations = value; } }

		public static void Animate(NetState state, PacketReader pvSrc)
		{
			Mobile from = state.Mobile;
			int action = pvSrc.ReadInt32();

			bool ok = false;

			for (int i = 0; !ok && i < m_ValidAnimations.Length; ++i)
				ok = (action == m_ValidAnimations[i]);

			if (from != null && ok && from.Alive && from.Body.IsHuman && !from.Mounted)
				from.Animate(action, 7, 1, true, false, 0);
		}

		public static void QuestArrow(NetState state, PacketReader pvSrc)
		{
			bool rightClick = pvSrc.ReadBoolean();
			Mobile from = state.Mobile;

			if (from != null && from.QuestArrow != null)
				from.QuestArrow.OnClick(rightClick);
		}

		public static void ExtendedCommand(NetState state, PacketReader pvSrc)
		{
			int packetID = pvSrc.ReadUInt16();

			PacketHandler ph = GetExtendedHandler(packetID);

			if (ph != null)
			{
				if (ph.Ingame && state.Mobile == null)
				{
					Console.WriteLine("Client: {0}: Sent ingame packet (0xBFx{1:X2}) before having been attached to a mobile", state, packetID);
					state.Dispose();
				}
				else if (ph.Ingame && state.Mobile.Deleted)
				{
					state.Dispose();
				}
				else
				{
					ph.OnReceive(state, pvSrc);
				}
			}
			else
			{
				pvSrc.Trace(state);
			}
		}

		public static void CastSpell(NetState state, PacketReader pvSrc)
		{
			Mobile from = state.Mobile;

			if (from == null)
				return;

			Item spellbook = null;

			if (pvSrc.ReadInt16() == 1)
				spellbook = World.FindItem(pvSrc.ReadInt32());

			int spellID = pvSrc.ReadInt16() - 1;

			EventSink.InvokeCastSpellRequest(new CastSpellRequestEventArgs(from, spellID, spellbook));
		}

		public static void BatchQueryProperties(NetState state, PacketReader pvSrc)
		{
			if (!ObjectPropertyList.Enabled)
				return;

			Mobile from = state.Mobile;

			int length = pvSrc.Size - 3;

			if (length < 0 || (length % 4) != 0)
				return;

			int count = length / 4;

			for (int i = 0; i < count; ++i)
			{
				Serial s = pvSrc.ReadInt32();

				if (s.IsMobile)
				{
					Mobile m = World.FindMobile(s);

					if (m != null && from.CanSee(m) && Utility.InUpdateRange(from, m))
						m.SendPropertiesTo(from);
				}
				else if (s.IsItem)
				{
					Item item = World.FindItem(s);

					if (item != null && !item.Deleted && from.CanSee(item) && Utility.InUpdateRange(from.Location, item.GetWorldLocation()))
						item.SendPropertiesTo(from);
				}
			}
		}

		public static void QueryProperties(NetState state, PacketReader pvSrc)
		{
			if (!ObjectPropertyList.Enabled)
				return;

			Mobile from = state.Mobile;

			Serial s = pvSrc.ReadInt32();

			if (s.IsMobile)
			{
				Mobile m = World.FindMobile(s);

				if (m != null && from.CanSee(m) && Utility.InUpdateRange(from, m))
					m.SendPropertiesTo(from);
			}
			else if (s.IsItem)
			{
				Item item = World.FindItem(s);

				if (item != null && !item.Deleted && from.CanSee(item) && Utility.InUpdateRange(from.Location, item.GetWorldLocation()))
					item.SendPropertiesTo(from);
			}
		}

		public static void PartyMessage(NetState state, PacketReader pvSrc)
		{
			if (state.Mobile == null)
				return;

			switch (pvSrc.ReadByte())
			{
				case 0x01: PartyMessage_AddMember(state, pvSrc); break;
				case 0x02: PartyMessage_RemoveMember(state, pvSrc); break;
				case 0x03: PartyMessage_PrivateMessage(state, pvSrc); break;
				case 0x04: PartyMessage_PublicMessage(state, pvSrc); break;
				case 0x06: PartyMessage_SetCanLoot(state, pvSrc); break;
				case 0x08: PartyMessage_Accept(state, pvSrc); break;
				case 0x09: PartyMessage_Decline(state, pvSrc); break;
				default: pvSrc.Trace(state); break;
			}
		}

		public static void PartyMessage_AddMember(NetState state, PacketReader pvSrc)
		{
			if (PartyCommands.Handler != null)
				PartyCommands.Handler.OnAdd(state.Mobile);
		}

		public static void PartyMessage_RemoveMember(NetState state, PacketReader pvSrc)
		{
			if (PartyCommands.Handler != null)
				PartyCommands.Handler.OnRemove(state.Mobile, World.FindMobile(pvSrc.ReadInt32()));
		}

		public static void PartyMessage_PrivateMessage(NetState state, PacketReader pvSrc)
		{
			if (PartyCommands.Handler != null)
				PartyCommands.Handler.OnPrivateMessage(state.Mobile, World.FindMobile(pvSrc.ReadInt32()), pvSrc.ReadUnicodeStringSafe());
		}

		public static void PartyMessage_PublicMessage(NetState state, PacketReader pvSrc)
		{
			if (PartyCommands.Handler != null)
				PartyCommands.Handler.OnPublicMessage(state.Mobile, pvSrc.ReadUnicodeStringSafe());
		}

		public static void PartyMessage_SetCanLoot(NetState state, PacketReader pvSrc)
		{
			if (PartyCommands.Handler != null)
				PartyCommands.Handler.OnSetCanLoot(state.Mobile, pvSrc.ReadBoolean());
		}

		public static void PartyMessage_Accept(NetState state, PacketReader pvSrc)
		{
			if (PartyCommands.Handler != null)
				PartyCommands.Handler.OnAccept(state.Mobile, World.FindMobile(pvSrc.ReadInt32()));
		}

		public static void PartyMessage_Decline(NetState state, PacketReader pvSrc)
		{
			if (PartyCommands.Handler != null)
				PartyCommands.Handler.OnDecline(state.Mobile, World.FindMobile(pvSrc.ReadInt32()));
		}

		public static void StunRequest(NetState state, PacketReader pvSrc)
		{
			EventSink.InvokeStunRequest(new StunRequestEventArgs(state.Mobile));
		}

		public static void DisarmRequest(NetState state, PacketReader pvSrc)
		{
			EventSink.InvokeDisarmRequest(new DisarmRequestEventArgs(state.Mobile));
		}

		public static void StatLockChange(NetState state, PacketReader pvSrc)
		{
			int stat = pvSrc.ReadByte();
			int lockValue = pvSrc.ReadByte();

			if (lockValue > 2) lockValue = 0;

			Mobile m = state.Mobile;

			if (m != null)
			{
				switch (stat)
				{
					case 0: m.StrLock = (StatLockType)lockValue; break;
					case 1: m.DexLock = (StatLockType)lockValue; break;
					case 2: m.IntLock = (StatLockType)lockValue; break;
				}
			}
		}

		public static void ScreenSize(NetState state, PacketReader pvSrc)
		{
			int width = pvSrc.ReadInt32();
			int unk = pvSrc.ReadInt32();
		}

		public static void ContextMenuResponse(NetState state, PacketReader pvSrc)
		{
			Mobile from = state.Mobile;

			if (from != null)
			{
				ContextMenu menu = from.ContextMenu;

				from.ContextMenu = null;

				if (menu != null && from != null && from == menu.From)
				{
					IEntity entity = World.FindEntity(pvSrc.ReadInt32());

					if (entity != null && entity == menu.Target && from.CanSee(entity))
					{
						Point3D p;

						if (entity is Mobile)
							p = entity.Location;
						else if (entity is Item)
							p = ((Item)entity).GetWorldLocation();
						else
							return;

						int index = pvSrc.ReadUInt16();

						if (index >= 0 && index < menu.Entries.Length)
						{
							ContextMenuEntry e = menu.Entries[index];

							int range = e.Range;

							if (range == -1)
								range = 18;

							if (e.Enabled && from.InRange(p, range))
								e.OnClick();
						}
					}
				}
			}
		}

		public static void ContextMenuRequest(NetState state, PacketReader pvSrc)
		{
			Mobile from = state.Mobile;
			IEntity target = World.FindEntity(pvSrc.ReadInt32());

			if (from != null && target != null && from.Map == target.Map && from.CanSee(target))
			{
				if (target is Mobile && !Utility.InUpdateRange(from.Location, target.Location))
					return;
				else if (target is Item && !Utility.InUpdateRange(from.Location, ((Item)target).GetWorldLocation()))
					return;

				if (!from.CheckContextMenuDisplay(target))
					return;

				ContextMenu c = new ContextMenu(from, target);

				if (c.Entries.Length > 0)
				{
					if (target is Item)
					{
						object root = ((Item)target).RootParent;

						if (root is Mobile && root != from && ((Mobile)root).AccessLevel >= from.AccessLevel)
						{
							for (int i = 0; i < c.Entries.Length; ++i)
							{
								if (!c.Entries[i].NonLocalUse)
									c.Entries[i].Enabled = false;
							}
						}
					}

					from.ContextMenu = c;
				}
			}
		}

		public static void CloseStatus(NetState state, PacketReader pvSrc)
		{
			Serial serial = pvSrc.ReadInt32();
		}

		public static void Language(NetState state, PacketReader pvSrc)
		{
			string lang = pvSrc.ReadString(4);

			if (state.Mobile != null)
				state.Mobile.Language = lang;
		}

		public static void AssistVersion(NetState state, PacketReader pvSrc)
		{
			int unk = pvSrc.ReadInt32();
			string av = pvSrc.ReadString();
		}

		public static void ClientVersion(NetState state, PacketReader pvSrc)
		{
			CV version = state.Version = new CV(pvSrc.ReadString());

			EventSink.InvokeClientVersionReceived(new ClientVersionReceivedArgs(state, version));
		}

		public static void MobileQuery(NetState state, PacketReader pvSrc)
		{
			Mobile from = state.Mobile;

			pvSrc.ReadInt32(); // 0xEDEDEDED
			int type = pvSrc.ReadByte();
			Mobile m = World.FindMobile(pvSrc.ReadInt32());

			if (m != null)
			{
				switch (type)
				{
					case 0x00: // Unknown, sent by godclient
						{
							if (VerifyGC(state))
								Console.WriteLine("God Client: {0}: Query 0x{1:X2} on {2} '{3}'", state, type, m.Serial, m.Name);

							break;
						}
					case 0x04: // Stats
						{
							m.OnStatsQuery(from);
							break;
						}
					case 0x05:
						{
							m.OnSkillsQuery(from);
							break;
						}
					default:
						{
							pvSrc.Trace(state);
							break;
						}
				}
			}
		}

		private class LoginTimer : Timer
		{
			private NetState m_State;
			private Mobile m_Mobile;

			public LoginTimer(NetState state, Mobile m)
				: base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0))
			{
				m_State = state;
				m_Mobile = m;
			}

			protected override void OnTick()
			{
				if (m_State == null)
					Stop();
				if (m_State.Version != null)
				{
					m_State.BlockAllPackets = false;
					DoLogin(m_State, m_Mobile);
					Stop();
				}
			}
		}

		public static void PlayCharacter(NetState state, PacketReader pvSrc)
		{
			pvSrc.ReadInt32(); // 0xEDEDEDED

			string name = pvSrc.ReadString(30);

			pvSrc.Seek(2, SeekOrigin.Current);
			int flags = pvSrc.ReadInt32();
			pvSrc.Seek(24, SeekOrigin.Current);

			int charSlot = pvSrc.ReadInt32();
			int clientIP = pvSrc.ReadInt32();

			IAccount a = state.Account;

			if (a == null || charSlot < 0 || charSlot >= a.Length)
			{
				state.Dispose();
			}
			else
			{
				Mobile m = a[charSlot];

				// Check if anyone is using this account
				for (int i = 0; i < a.Length; ++i)
				{
					Mobile check = a[i];

					if (check != null && check.Map != Map.Internal && check != m)
					{
						Console.WriteLine("Login: {0}: Account in use", state);
						state.Send(new PopupMessage(PMMessage.CharInWorld));
						return;
					}
				}

				if (m == null)
				{
					state.Dispose();
				}
				else
				{
					if (m.NetState != null)
						m.NetState.Dispose();

					NetState.ProcessDisposedQueue();

					state.Send(new ClientVersionReq());

					state.BlockAllPackets = true;

					state.Flags = flags;

					state.Mobile = m;
					m.NetState = state;

					new LoginTimer(state, m).Start();
				}
			}
		}

		public static void DoLogin(NetState state, Mobile m)
		{
			state.Send(new LoginConfirm(m));

			if (m.Map != null)
				state.Send(new MapChange(m));

			state.Send(new MapPatches());

			state.Send(SeasonChange.Instantiate(m.GetSeason(), true));

			state.Send(SupportedFeatures.Instantiate(state.Account));

			state.Sequence = 0;
			state.Send(new MobileUpdate(m));
			state.Send(new MobileUpdate(m));

			m.CheckLightLevels(true);

			state.Send(new MobileUpdate(m));

				
			//state.Send( new MobileAttributes( m ) );
			state.Send(new MobileStatus(m, m));
			state.Send(Server.Network.SetWarMode.Instantiate(m.Warmode));

			m.SendEverything();

			state.Send(SupportedFeatures.Instantiate(state.Account));
			state.Send(new MobileUpdate(m));
			//state.Send( new MobileAttributes( m ) );
			state.Send(new MobileStatus(m, m));
			state.Send(Server.Network.SetWarMode.Instantiate(m.Warmode));
			state.Send(new MobileIncoming(m, m));

			state.Send(LoginComplete.Instance);
			state.Send(new CurrentTime());
			state.Send(SeasonChange.Instantiate(m.GetSeason(), true));
			state.Send(new MapChange(m));

			EventSink.InvokeLogin(new LoginEventArgs(m));

			m.ClearFastwalkStack();
		}

		public static void CreateCharacter(NetState state, PacketReader pvSrc)
		{
			int unk1 = pvSrc.ReadInt32();
			int unk2 = pvSrc.ReadInt32();
			int unk3 = pvSrc.ReadByte();
			string name = pvSrc.ReadString(30);

			pvSrc.Seek(2, SeekOrigin.Current);
			int flags = pvSrc.ReadInt32();
			pvSrc.Seek(8, SeekOrigin.Current);
			int prof = pvSrc.ReadByte();
			pvSrc.Seek(15, SeekOrigin.Current);

			//bool female = pvSrc.ReadBoolean();

			int genderRace = pvSrc.ReadByte();

			int str = pvSrc.ReadByte();
			int dex = pvSrc.ReadByte();
			int intl = pvSrc.ReadByte();
			int is1 = pvSrc.ReadByte();
			int vs1 = pvSrc.ReadByte();
			int is2 = pvSrc.ReadByte();
			int vs2 = pvSrc.ReadByte();
			int is3 = pvSrc.ReadByte();
			int vs3 = pvSrc.ReadByte();
			int hue = pvSrc.ReadUInt16();
			int hairVal = pvSrc.ReadInt16();
			int hairHue = pvSrc.ReadInt16();
			int hairValf = pvSrc.ReadInt16();
			int hairHuef = pvSrc.ReadInt16();
			pvSrc.ReadByte();
			int cityIndex = pvSrc.ReadByte();
			int charSlot = pvSrc.ReadInt32();
			int clientIP = pvSrc.ReadInt32();
			int shirtHue = pvSrc.ReadInt16();
			int pantsHue = pvSrc.ReadInt16();

			bool female = ((genderRace % 2) != 0);

			Race race = Race.Races[(byte)(genderRace / 2)];

			if (race == null)
				race = Race.DefaultRace;

			CityInfo[] info = state.CityInfo;
			IAccount a = state.Account;

			if (info == null || a == null || cityIndex < 0 || cityIndex >= info.Length)
			{
				state.Dispose();
			}
			else
			{
				// Check if anyone is using this account
				for (int i = 0; i < a.Length; ++i)
				{
					Mobile check = a[i];

					if (check != null && check.Map != Map.Internal)
					{
						Console.WriteLine("Login: {0}: Account in use", state);
						state.Send(new PopupMessage(PMMessage.CharInWorld));
						return;
					}
				}

				state.Flags = flags;

				CharacterCreatedEventArgs args = new CharacterCreatedEventArgs(
					state, a,
					name, female, hue,
					str, dex, intl,
					info[cityIndex],
					new SkillNameValue[3]
					{
						new SkillNameValue( (SkillName)is1, vs1 ),
						new SkillNameValue( (SkillName)is2, vs2 ),
						new SkillNameValue( (SkillName)is3, vs3 ),
					},
					shirtHue, pantsHue,
					hairVal, hairHue,
					hairValf, hairHuef,
					prof,
					race
					);

				state.Send(new ClientVersionReq());

				state.BlockAllPackets = true;

				EventSink.InvokeCharacterCreated(args);

				Mobile m = args.Mobile;

				if (m != null)
				{
					state.Mobile = m;
					m.NetState = state;
					new LoginTimer(state, m).Start();
				}
				else
				{
					state.BlockAllPackets = false;
					state.Dispose();
				}
			}
		}

		private const int AuthIDWindowSize = 128;
		private static int[] m_AuthIDWindow = new int[AuthIDWindowSize];
		private static DateTime[] m_AuthIDWindowAge = new DateTime[AuthIDWindowSize];

		private static bool m_ClientVerification = true;

		public static bool ClientVerification
		{
			get { return m_ClientVerification; }
			set { m_ClientVerification = value; }
		}

		private static int GenerateAuthID()
		{
			int authID = Utility.Random(1, int.MaxValue - 1);

			if (Utility.RandomBool())
				authID |= 1 << 31;

			bool wasSet = false;
			DateTime oldest = DateTime.MaxValue;
			int oldestIndex = 0;

			for (int i = 0; i < m_AuthIDWindow.Length; ++i)
			{
				if (m_AuthIDWindow[i] == 0)
				{
					m_AuthIDWindow[i] = authID;
					m_AuthIDWindowAge[i] = DateTime.Now;
					wasSet = true;
					break;
				}
				else if (m_AuthIDWindowAge[i] < oldest)
				{
					oldest = m_AuthIDWindowAge[i];
					oldestIndex = i;
				}
			}

			if (!wasSet)
			{
				m_AuthIDWindow[oldestIndex] = authID;
				m_AuthIDWindowAge[oldestIndex] = DateTime.Now;
			}

			return authID;
		}

		private static bool IsValidAuthID(int authID)
		{
			for (int i = 0; i < m_AuthIDWindow.Length; ++i)
			{
				if (m_AuthIDWindow[i] == authID)
				{
					m_AuthIDWindow[i] = 0;
					return true;
				}
			}

			return !m_ClientVerification;
		}

		public static void GameLogin(NetState state, PacketReader pvSrc)
		{
			if (state.SentFirstPacket)
			{
				state.Dispose();
				return;
			}

			state.SentFirstPacket = true;

			int authID = pvSrc.ReadInt32();

			if (!IsValidAuthID(authID))
			{
				Console.WriteLine("Login: {0}: Invalid client detected, disconnecting", state);
				state.Dispose();
				return;
			}
			else if (state.m_AuthID != 0 && authID != state.m_AuthID)
			{
				Console.WriteLine("Login: {0}: Invalid client detected, disconnecting", state);
				state.Dispose();
				return;
			}
			else if (state.m_AuthID == 0 && authID != state.m_Seed)
			{
				Console.WriteLine("Login: {0}: Invalid client detected, disconnecting", state);
				state.Dispose();
				return;
			}

			string username = pvSrc.ReadString(30);
			string password = pvSrc.ReadString(30);

			GameLoginEventArgs e = new GameLoginEventArgs(state, username, password);

			EventSink.InvokeGameLogin(e);

			if (e.Accepted)
			{
				state.CityInfo = e.CityInfo;
				state.CompressionEnabled = true;

				if (Core.AOS)
					state.Send(SupportedFeatures.Instantiate(state.Account));

				state.Send(new CharacterList(state.Account, state.CityInfo));
			}
			else
			{
				state.Dispose();
			}
		}

		public static void PlayServer(NetState state, PacketReader pvSrc)
		{
			int index = pvSrc.ReadInt16();
			ServerInfo[] info = state.ServerInfo;
			IAccount a = state.Account;

			if (info == null || a == null || index < 0 || index >= info.Length)
			{
				state.Dispose();
			}
			else
			{
				ServerInfo si = info[index];

				state.m_AuthID = PlayServerAck.m_AuthID = GenerateAuthID();

				state.SentFirstPacket = false;
				state.Send(new PlayServerAck(si));
			}
		}

		public static void LoginServerSeed(NetState state, PacketReader pvSrc)
		{
			state.m_Seed = pvSrc.ReadInt32();
			state.Seeded = true;

			if (state.m_Seed == 0)
			{
				Console.WriteLine("Login: {0}: Invalid client detected, disconnecting", state);
				state.Dispose();
				return;
			}

			int clientMaj = pvSrc.ReadInt32();
			int clientMin = pvSrc.ReadInt32();
			int clientRev = pvSrc.ReadInt32();
			int clientPat = pvSrc.ReadInt32();

			state.Version = new ClientVersion(clientMaj, clientMin, clientRev, clientPat);
		}

		public static void AccountLogin(NetState state, PacketReader pvSrc)
		{
			if (state.SentFirstPacket)
			{
				state.Dispose();
				return;
			}

			state.SentFirstPacket = true;

			string username = pvSrc.ReadString(30);
			string password = pvSrc.ReadString(30);

			AccountLoginEventArgs e = new AccountLoginEventArgs(state, username, password);

			EventSink.InvokeAccountLogin(e);

			if (e.Accepted)
				AccountLogin_ReplyAck(state);
			else
				AccountLogin_ReplyRej(state, e.RejectReason);
		}

		public static void AccountLogin_ReplyAck(NetState state)
		{
			ServerListEventArgs e = new ServerListEventArgs(state, state.Account);

			EventSink.InvokeServerList(e);

			if (e.Rejected)
			{
				state.Account = null;
				state.Send(new AccountLoginRej(ALRReason.BadComm));
				state.Dispose();
			}
			else
			{
				ServerInfo[] info = e.Servers.ToArray();

				state.ServerInfo = info;

				state.Send(new AccountLoginAck(info));
			}
		}

		public static void AccountLogin_ReplyRej(NetState state, ALRReason reason)
		{
			state.Send(new AccountLoginRej(reason));
			state.Dispose();
		}
	}
}